/**
 * 
 */
package neptune.manager.utils;

import java.util.ArrayList;

/**
 * Gestisce un meccanismo di logging ad albero. Il meccanismo e' da intendersi
 * non per motivi di debug, quanto per gestire situazioni in cui l'utente ha
 * necessita' di conoscere cosa ha fatto il software.
 * 
 * <br>
 * <br>
 * La classe Logger crea un root LogContext che puo' essere adoperato per
 * generare un albero di LogContext. Ciascun LogContext contiene dei LogMessage,
 * che sono:<br>
 * - NORMAL : per messaggi di log di monitoraggio<br>
 * - WARNING : per messaggi di warning<br>
 * - ERROR : per messaggi di errore<br>
 * - CONTEXT : per indicare che il messaggio e' in realta' un contesto
 * innestato.<br>
 * <br>
 * La creazione di un contesto innestato (e quindi di un messaggio di tipo
 * CONTEXT) e' fatta adoperando il metodo {@link LogContext#createChild(String)}
 * .<br>
 * Questi messaggi sono incapsulati nella classe {@link ContextMessage}.
 * 
 * @author Roberto Bifulco [RobertoBifulco.it] [info@robertoBifulco.it]
 * 
 */
public class Logger {

	/**
	 * 
	 * @author Roberto Bifulco [RobertoBifulco.it] [info@robertoBifulco.it]
	 * 
	 */
	public static enum MessageType {
		NORMAL, WARNING, ERROR, CONTEXT;
	}

	/**
	 * 
	 * @author Roberto Bifulco [RobertoBifulco.it] [info@robertoBifulco.it]
	 * 
	 */
	public static class LogMessage {

		private String message;
		private MessageType type;

		public LogMessage(String message, MessageType type) {
			this.message = message;
			this.type = type;
		}

		/**
		 * @return the message
		 */
		public String getMessage() {
			return message;
		}

		/**
		 * @return the type
		 */
		public MessageType getType() {
			return type;
		}

	}

	public static class ContextMessage extends LogMessage {

		private LogContext context;

		private ContextMessage(String message, MessageType type) {
			super(message, type);
		}

		public ContextMessage(LogContext context) {
			this(context.id, MessageType.CONTEXT);
			this.context = context;
		}

		/**
		 * @return the context
		 */
		public LogContext getContext() {
			return context;
		}

	}

	/**
	 * 
	 * @author Roberto Bifulco [RobertoBifulco.it] [info@robertoBifulco.it]
	 * 
	 */
	public static class LogContext {

		private String id;
		private ArrayList<LogMessage> messages = new ArrayList<LogMessage>(0);
		private boolean open;
		private ArrayList<LogContext> children = new ArrayList<LogContext>(0);

		public LogContext(String id) {
			super();
			this.id = id;
			open = true;
		}

		/**
		 * @return the id
		 */
		public String getId() {
			return id;
		}

		public void addMessage(String message, MessageType type) {
			messages.add(new LogMessage(message, type));
		}

		public LogMessage[] getMessages() {
			return messages.toArray(new LogMessage[] {});
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.lang.Object#equals(java.lang.Object)
		 */
		@Override
		public boolean equals(Object obj) {
			return id.equals(((LogContext) obj).id);
		}

		public void closeContext() {
			open = false;
		}

		/**
		 * @return the open
		 */
		public boolean isOpen() {
			return open;
		}

		/**
		 * @return the children
		 */
		public ArrayList<LogContext> getChildren() {
			return children;
		}

		public LogContext createChild(String childId) {
			LogContext child = new LogContext(id + "." + childId);
			children.add(child);
			ContextMessage message = new ContextMessage(child);
			messages.add(message);
			return child;
		}
		
		public void addContextMessage(LogContext toAdd){
			messages.add(new ContextMessage(toAdd));
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.lang.Object#toString()
		 */
		@Override
		public String toString() {
			String messageStr = "Context: " + id;
			for (LogMessage message : messages) {
				switch (message.getType()) {
				case ERROR:
					messageStr += "\nERROR: ";
					break;
				case WARNING:
					messageStr += "\nWARNING: ";
				case CONTEXT:
					messageStr += "\n	"
							+ ((ContextMessage) message).getContext();
				default:
					messageStr += "\n";
					break;
				}

				messageStr += message.getMessage();
			}
			return messageStr;
		}

		public String prettyPrint(int indentation) {
			String indent = "";
			for (int i = 0; i < indentation; i++)
				indent += "\t";
			String messageStr = indent + "Context: " + id;
			for (LogMessage message : messages) {
				switch (message.getType()) {
				case ERROR:
					messageStr += "\n" + indent + "\tERROR: ";
					messageStr += message.getMessage();
					break;
				case WARNING:
					messageStr += "\n" + indent + "\tWARNING: ";
					messageStr += message.getMessage();
					break;
				case CONTEXT:
					messageStr += "\n"
							+ ((ContextMessage) message).getContext()
									.prettyPrint(indentation + 1);
					break;
				default:
					messageStr += "\n\t" + indent;
					messageStr += message.getMessage();
					break;
				}
			}

			return messageStr;
		}

	}

	/**
	 * 
	 * @param id
	 * @return
	 */
	public static LogContext createLogContext(String id) {
		LogContext context = new LogContext(id);
		return context;
	}
}
